// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: hyuan

#include "coin2/exchange/deribit_base/api_order/native_rest_async_asio_subscriber.h"

#include <cstdlib>
#include <iostream>
#include <set>
#include <sstream>
#include <string>
#include <thread>

#include "coin2/base/log.h"

namespace coin2::exchange::deribit_futures::api_order {

void DeribitFuturesRestAsyncAsioSubscriber::QueryBalance() {
  auto http_context = rest_client()->QueryAccountBalance();
  if (!http_context) return;
  http_context->handler =
      std::bind(&DeribitFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void DeribitFuturesRestAsyncAsioSubscriber::QueryAccountSummary(const std::string& currency) {
  auto http_context = rest_client()->QueryAccountSummary(currency);
  if (!http_context) return;
  http_context->handler =
      std::bind(&DeribitFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void DeribitFuturesRestAsyncAsioSubscriber::QueryPosition(const std::string& currency) {
  RestParams params;
  params.set_currency(currency);
  auto http_context = rest_client()->QueryAccountPosition(params);
  if (!http_context) return;
  http_context->handler =
      std::bind(&DeribitFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void DeribitFuturesRestAsyncAsioSubscriber::QueryOpenOrders(const std::string& native_currency) {
  RestParams params;
  params.set_currency(native_currency);
  auto http_context = rest_client()->QueryOpenOrders(params);
  if (!http_context) return;
  http_context->handler =
      std::bind(&DeribitFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void DeribitFuturesRestAsyncAsioSubscriber::QueryHistoryOrders(const std::string& native_currency) {
  RestParams params;
  params.set_currency(native_currency);
  auto http_context = rest_client()->QueryHistoryOrders(params);
  if (!http_context) return;
  http_context->handler =
      std::bind(&DeribitFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void DeribitFuturesRestAsyncAsioSubscriber::QueryFills(const std::string& native_currency) {
  RestParams params;
  params.set_currency(native_currency);
  auto http_context = rest_client()->QueryTrades(params);
  if (!http_context) return;
  http_context->handler =
      std::bind(&DeribitFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void DeribitFuturesRestAsyncAsioSubscriber::GenerateOrRefreshAuth() {
  auto http_context = rest_client()->GenerateOrRefreshAuth();
  if (!http_context) return;
  http_context->handler =
      std::bind(&DeribitFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void DeribitFuturesRestAsyncAsioSubscriber::QueryDeposits(const std::string& native_currency) {
  RestParams params;
  params.set_currency(native_currency);
  auto http_context = rest_client()->QueryDeposits(params);
  if (!http_context) return;
  http_context->handler =
      std::bind(&DeribitFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void DeribitFuturesRestAsyncAsioSubscriber::QueryWithdrawals(const std::string& native_currency) {
  RestParams params;
  params.set_currency(native_currency);
  auto http_context = rest_client()->QueryWithdrawals(params);
  if (!http_context) return;
  http_context->handler =
      std::bind(&DeribitFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void DeribitFuturesRestAsyncAsioSubscriber::QueryTransfers(const std::string& native_currency) {
  RestParams params;
  params.set_currency(native_currency);
  auto http_context = rest_client()->QueryTransfers(params);
  if (!http_context) return;
  http_context->handler =
      std::bind(&DeribitFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void DeribitFuturesRestAsyncAsioSubscriber::SubscribeAsync() {
  LOG(INFO) << "Deribit Futures rest subscriber config: " << config_.DebugString();

  AddPeriodicTask(
      std::bind(&DeribitFuturesRestAsyncAsioSubscriber::GenerateOrRefreshAuth, this),
      1,
      "query_auth");

  std::stringstream ss;
  std::set<std::string> native_currencies;
  for (const auto* currency : symcache_->GetCurrencies()) {
    const auto& native_currency = currency->order_currency();
    if (symcache_->is_options() && options_supported_currencies_.count(native_currency) == 0) {
      continue;
    }
    if (native_currencies.count(native_currency)) {
      continue;
    }
    ss << native_currency << " ";
    native_currencies.emplace(native_currency);
  }
  SPDLOG_INFO(
      "[{}] rest subscribe currency: {}",
      (symcache_->is_futures() ? "Futures" : "Options"),
      ss.str());

  AddPeriodicTask(
      std::bind(
          &DeribitFuturesRestAsyncAsioSubscriber::QueryAccountSummary,
          this,
          std::placeholders::_1),
      config_.query_account_balance_period_sec(),
      "query_balance_summary",
      native_currencies);

  AddPeriodicTask(
      std::bind(&DeribitFuturesRestAsyncAsioSubscriber::QueryPosition, this, std::placeholders::_1),
      config_.query_account_position_period_sec(),
      "query_position",
      native_currencies);

  if (is_readonly_order_system_) {
    AddPeriodicTask(
        std::bind(
            &DeribitFuturesRestAsyncAsioSubscriber::QueryTransfers,
            this,
            std::placeholders::_1),
        config_.query_account_balance_period_sec(),
        "query_transfer",
        native_currencies);
    AddPeriodicTask(
        std::bind(
            &DeribitFuturesRestAsyncAsioSubscriber::QueryDeposits,
            this,
            std::placeholders::_1),
        config_.query_account_balance_period_sec(),
        "query_deposits",
        native_currencies);
    AddPeriodicTask(
        std::bind(
            &DeribitFuturesRestAsyncAsioSubscriber::QueryWithdrawals,
            this,
            std::placeholders::_1),
        config_.query_account_balance_period_sec(),
        "query_withdrawals",
        native_currencies);
    return;
  }

  AddPeriodicTask(
      std::bind(
          &DeribitFuturesRestAsyncAsioSubscriber::QueryOpenOrders,
          this,
          std::placeholders::_1),
      config_.query_open_orders_period_sec(),
      "query_open_orders",
      native_currencies);

  AddPeriodicTask(
      std::bind(
          &DeribitFuturesRestAsyncAsioSubscriber::QueryHistoryOrders,
          this,
          std::placeholders::_1),
      config_.query_cancel_orders_period_sec(),
      "query_history_orders",
      native_currencies);

  AddPeriodicTask(
      std::bind(&DeribitFuturesRestAsyncAsioSubscriber::QueryFills, this, std::placeholders::_1),
      config_.query_fill_period_sec(),
      "query_fills",
      native_currencies);
}

}  // namespace coin2::exchange::deribit_futures::api_order
